﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.IO;

namespace Tron.Players.Strategic
{
    class StrategicPlayerHelper
    {
        public List<Vector2> GetPossibleMoveVectorsOnBoard(int[,] board, Vector2 playerLocation)
        {
            List<Vector2> result = new List<Vector2>();
            foreach (Vector2 possibleMoveDirection in Constants.PossibleMoveDirections)
            {
                Vector2 v = playerLocation + possibleMoveDirection;
                try
                {
                    if (board[(int)v.X, (int)v.Y] == -2)
                        result.Add(possibleMoveDirection);
                }
                catch (Exception)
                {
                }
            }
            return result;
        }

        public List<Vector2> GetPossibleMoveVectorsOnBoardClockWiseFromDirection(int[,] board, Vector2 playerLocation, Vector2 playerDirection)
        {
            List<Vector2> possibleMoveVectors = GetPossibleMoveVectorsOnBoard(board, playerLocation);
            List<Vector2> orderedPossibleMoveVectors = new List<Vector2>();

            int index = Constants.PossibleMoveDirections.IndexOf(playerDirection) - 1;
            for (int i = 0; i < 4; i++)
            {
                if (index > 3)
                {
                    index = 0;
                }
                if (index < 0)
                {
                    index = 3;
                }
                if (possibleMoveVectors.Contains(Constants.PossibleMoveDirections[index]))
                {
                    orderedPossibleMoveVectors.Add(Constants.PossibleMoveDirections[index]);
                }
                index++;
            }
            return orderedPossibleMoveVectors;
        }

        public double GetNumberOfSquaresThatCanBeReachedBeforeOtherPlayer(int[,] board, Vector2 playerLocation, Vector2 opponentLocation, bool opponentAlive)
        {
            int columns = Constants.Columns;
            int rows = Constants.Rows;
            int totalSquareCount = columns * rows;

            int playerSquaresCount = 0;
            int opponentSquaresCount = 0;

            bool[,] playerCanReachBeforeOpponent = new bool[columns, rows];
            bool[,] opponentCanReachBeforePlayer = new bool[columns, rows];

            GetSquaresThatCanBeReachedBeforeOtherPlayer(board, playerLocation, opponentLocation, opponentAlive,
                playerCanReachBeforeOpponent, opponentCanReachBeforePlayer);
            

            for(int x = 0; x < columns; x++)
            {
                for(int y = 0; y < rows; y++)
                {
                    if (playerCanReachBeforeOpponent[x, y])
                    {
                        playerSquaresCount++;
                    }
                    else if(opponentCanReachBeforePlayer[x,y])
                    {
                        opponentSquaresCount++;
                    }
                }
            }
            double returnValue = (playerSquaresCount - opponentSquaresCount) / (double)totalSquareCount;
            return returnValue;
        }

        public void GetSquaresThatCanBeReachedBeforeOtherPlayer(int[,] board, Vector2 playerLocation, Vector2 opponentLocation, bool opponentAlive,
            bool[,] playerCanReachBeforeOpponent, bool[,] opponentCanReachBeforePlayer)
        {
            int columns = Constants.Columns;
            int rows = Constants.Rows;

            int[,] playerMovesToReachLocation = new int[columns, rows];
            int[,] opponentMovesToReachLocation = new int[columns, rows];

            Vector2[,] playerFlowPaths = new Vector2[columns, rows];
            Vector2[,] opponentFlowPaths = new Vector2[columns, rows];

            bool[,] playerExpandedNode = new bool[columns, rows];
            bool[,] opponentExpandedNode = new bool[columns, rows];

            for(int x = 0; x < columns; x++)
            {
                for(int y = 0; y < rows; y++)
                {
                    playerMovesToReachLocation[x,y] = -1;
                    opponentMovesToReachLocation[x,y] = -1;

                    playerFlowPaths[x,y] = Vector2.One; // Using (1,1) as invalid
                    opponentFlowPaths[x,y] = Vector2.One;

                    playerExpandedNode[x,y] = false;
                    opponentExpandedNode[x,y] = false;

                    playerCanReachBeforeOpponent[x, y] = false;
                    opponentCanReachBeforePlayer[x, y] = false;
                }
            }

            GetPathsToAllLocations(board, playerLocation, playerMovesToReachLocation, playerFlowPaths, playerExpandedNode);
            if(opponentAlive)
                GetPathsToAllLocations(board, opponentLocation, opponentMovesToReachLocation, opponentFlowPaths, opponentExpandedNode);


            for (int x = 0; x < columns; x++)
            {
                for (int y = 0; y < rows; y++)
                {


                    if(playerExpandedNode[x,y])
                    {
                        if (!opponentExpandedNode[x, y])
                        {
                            // Only the player can reach the node.
                            playerCanReachBeforeOpponent[x, y] = true;
                        }
                        else if(playerMovesToReachLocation[x,y] < opponentMovesToReachLocation[x,y])
                        {
                            // The player can reach the node before the opponent.
                            playerCanReachBeforeOpponent[x, y] = true;
                        }
                        else if(playerMovesToReachLocation[x,y] > opponentMovesToReachLocation[x,y])
                        {
                            // The opponent can reach the node before the player.
                            opponentCanReachBeforePlayer[x,y] = true;
                        }
                    }
                    else if (opponentExpandedNode[x, y])
                    {
                        // Only the opponent can reach the node.
                        opponentCanReachBeforePlayer[x, y] = true;
                    }
                }
            }

            #region Text Output
            //TextWriter tw = new StreamWriter("strategicPlayerHelperOutput.txt");

            //tw.WriteLine("playerMovesToReachLocation");
            //for (int y = 0; y < rows; y++)
            //{
            //    String row = String.Empty;
            //    for (int x = 0; x < columns; x++)
            //    {
            //        if (playerMovesToReachLocation[x, y] == -1)
            //            row += " x ";
            //        else
            //        {
            //            String s = playerMovesToReachLocation[x, y].ToString();
            //            if (s.Length == 1)
            //                s = " " + s + " ";
            //            else if (s.Length == 2)
            //                s += " ";
            //            row += s;
            //        }
            //    }
            //    tw.WriteLine(row);
            //}


            //tw.WriteLine("playerExpandedNode");
            //for (int y = 0; y < rows; y++)
            //{
            //    String row = String.Empty;
            //    for (int x = 0; x < columns; x++)
            //    {
            //        if (playerExpandedNode[x, y])
            //            row += " 1 ";
            //        else
            //        {
            //            row += " 0 ";
            //        }
            //    }
            //    tw.WriteLine(row);
            //}


            //tw.WriteLine("canBeReachedBeforeOpponent");
            //for (int y = 0; y < rows; y++)
            //{
            //    String row = String.Empty;
            //    for (int x = 0; x < columns; x++)
            //    {
            //        if (canBeReachedBeforeOpponent[x, y])
            //            row += " 1 ";
            //        else
            //        {
            //            row += " 0 ";
            //        }
            //    }
            //    tw.WriteLine(row);
            //}


            //tw.Close();
            #endregion
        }

        public void GetPathsToAllLocations(int[,] board, Vector2 location,
            int[,] movesToReachLocation, Vector2[,] flowPaths, bool[,] expandedNode)
        {
            Queue<Vector2> nodesToExpand = new Queue<Vector2>();

            nodesToExpand.Enqueue(location);
            movesToReachLocation[(int)location.X, (int)location.Y] = 0;
            flowPaths[(int)location.X, (int)location.Y] = Vector2.Zero;
            expandedNode[(int)location.X, (int)location.Y] = true;

            while (nodesToExpand.Count > 0)
            {
                Vector2 nodeToExpand = nodesToExpand.Dequeue();

                foreach (Vector2 possibleMoveDirection in Constants.PossibleMoveDirections)
                {
                    Vector2 position = nodeToExpand + possibleMoveDirection;
                    int x = (int)position.X;
                    int y = (int)position.Y;

                    if (!expandedNode[x, y])
                    {
                        if (board[(int)position.X, (int)position.Y] == -2)
                        {
                            // Add this square to the list of those that will be expanded.
                            nodesToExpand.Enqueue(position);

                            movesToReachLocation[(int)position.X, (int)position.Y] =
                                movesToReachLocation[(int)nodeToExpand.X, (int)nodeToExpand.Y] + 1;

                            flowPaths[(int)position.X, (int)position.Y] = -possibleMoveDirection;

                            expandedNode[(int)position.X, (int)position.Y] = true;
                        }
                    }
                }
            }
        }

        public bool IsOpponentInSeperateComponent(int[,] board,
            Vector2 playerLocation,Vector2 opponentLocation)
        {
            int columns = Constants.Columns;
            int rows = Constants.Rows;
            bool[,] expandedNode = new bool[columns, rows];

            Queue<Vector2> nodesToExpand = new Queue<Vector2>();

            nodesToExpand.Enqueue(playerLocation);
            expandedNode[(int)playerLocation.X, (int)playerLocation.Y] = true;

            while (nodesToExpand.Count > 0)
            {
                Vector2 nodeToExpand = nodesToExpand.Dequeue();

                foreach (Vector2 possibleMoveDirection in Constants.PossibleMoveDirections)
                {
                    Vector2 position = nodeToExpand + possibleMoveDirection;
                    int x = (int)position.X;
                    int y = (int)position.Y;

                    if (!expandedNode[x, y])
                    {
                        if (board[(int)position.X, (int)position.Y] == -2)
                        {
                            // Add this square to the list of those that will be expanded.
                            nodesToExpand.Enqueue(position);
                            expandedNode[(int)position.X, (int)position.Y] = true;
                        }
                        if (opponentLocation == position)
                        {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }
}